#include "Point.h"
#include "Shape.h"
#include "Circle.h"
#include "MyScreen.h"

bool Circle::legalRadius ( int radius) const
{
	if (radius < 0)
	{
		cerr << "Error: the radius of the circle must have positive value" << endl;
		return false;
	}

	return true;
}

bool Circle::isInBorders () const
{
	int left , right , down , up;

	left = (int)(m_sumDirX + m_point.getx() - m_radius);
	up = (int)(m_sumDirY + m_point.gety() - m_radius);
	right = (int)(m_sumDirX + m_point.getx() + m_radius);
	down = (int)(m_sumDirY + m_point.gety() + m_radius);

	if (left < MIN_SCREEN_X ||
		up < MIN_SCREEN_Y ||
		right >= MAX_SCREEN_X ||
		down >= MAX_SCREEN_Y )
		return false;
	return true;
}

void Circle::set( Point p1,  int radius)
{	
	Shape::set (p1, 0, 0);

	m_radius = radius;
}

void Circle::setRadius(int radius)
{

	m_radius = radius;
}

bool Circle::input()
{
	Point p1;
	int radius;
	p1.input();

	cout << "Please enter the radius of the Circle: ";
	cin >> radius;

	if (legalRadius(radius))
	{
		this->set (p1, radius);
		return true;
	}
	
	return false;
}

bool Circle::draw(char frameCh, bool fill, MyScreen * scr, 
					    bool split)
{
	int x, y;
	int modFactorX = MAX_SCREEN_X-1, modFactorY = MAX_SCREEN_Y-1;
	double currX, currY;

	x = m_point.getx();
	y = m_point.gety();

	// Adds to that value the movment axis (if there is no movment, the sumDir = 0)
	x += (int)m_sumDirX;
	y += (int)m_sumDirY;

	if (fill)
	{
		// for every radius lower then the original
		for (int i=0; i < m_radius; ++i)
			// print circle with blanks
			for ( int j=0; j <=90; j+= PRECISION)
			{
				currX = (cos( (double)j ) * i);
				currY = (sin( (double)j ) * i);

				print4Points (x, y, currX, currY, modFactorX, modFactorY, scr, split, ' ');
			}
	}

	for( int i = 0; i <= 90; i += PRECISION )
	{
		// Calculates the value that is needed to be added to the original center point
		currX = (cos( (double)i ) * m_radius);
		currY = (sin( (double)i ) * m_radius);

		print4Points (x, y, currX, currY, modFactorX, modFactorY, scr, split, frameCh);
	}

	return true;
}

void Circle::print4Points (	double x, double y, 
							double currXAxis, double currYAxis, 
							int modFactorX, int modFactorY, 
							MyScreen * scr, bool split, char frameCh)
{
	int printXLeft, printXRight, printYUpper, printYLower; 
	double xLeft, xRight, yUpper, yLower;

	currXAxis = abs(currXAxis);
	currYAxis = abs(currYAxis);

	// calculating the x left value
	xLeft = x - currXAxis;
	printXLeft = (int)(floor (xLeft + 0.5 ));

	// calculating the x right value
	xRight = x + currXAxis;
	printXRight = (int)(floor (xRight + 0.5 ));
	
	// calculating the y upper value
	yUpper = y - currYAxis;
	printYUpper = (int)(floor (yUpper + 0.5 ));

	// calculating the y lower value
	yLower = y + currYAxis;
	printYLower = (int)(floor (yLower + 0.5 ));

	if (split)
	{
		// for the modulu to work, the X and Y values should be bigger than the screen minimum (0)
		if (printXLeft < MIN_SCREEN_X)
		{
				// for the next itteration - let the sum (that is added to the x value) be 1 screen bigger
				m_sumDirX += (m_sumDirX < MIN_SCREEN_X ? (MAX_SCREEN_X-1) : 0); 
				printXLeft += (MAX_SCREEN_X-1);
		}

		if (printYUpper < MIN_SCREEN_Y)
		{
				m_sumDirY += (m_sumDirY < MIN_SCREEN_Y ? (MAX_SCREEN_Y-1) : 0); 
				printYUpper += (MAX_SCREEN_Y-1);
		}

		// the split of the screen action
		printXLeft %= modFactorX;
		printXRight %= modFactorX;
		printYUpper %= modFactorY;
		printYLower %= modFactorY;
	}

	Point::printPoint (Point (printXRight, printYLower), scr, frameCh);
	Point::printPoint (Point (printXLeft, printYLower), scr, frameCh);
	Point::printPoint (Point (printXRight, printYUpper), scr, frameCh);
	Point::printPoint (Point (printXLeft, printYUpper), scr, frameCh);
}

bool Circle::search(const Point& p1) const
{
	bool flag = false;
	int x = p1.getx();
	int y = p1.gety();
	int r = this->getRadius();

	int d = ((this->getXPoint() - x) * (this->getXPoint() - x)) + ((this->getYPoint() - y) * (this->getYPoint() - y));

	if (d <= r*r)
		return true;
	else
		return false;

}

void Circle::handleCollision (CRectangle * rec)
{
	HandleCollisions::handleCircleRectangleCollision (this, rec);
	return;
}

void Circle::handleCollision (Circle * circ)
{
	HandleCollisions::handleCircleCircleCollision (this, circ);
	return;
}

void Circle::handleCollision (Shape * shape)
{
	shape->handleCollision(this);
	return;
}

bool Circle::checkStuck (const MyScreen & scr) const
{
	return HandleCollisions::checkCircStuck(*this , scr);
}

bool Circle::writeToText(ofstream & file) 
{
	
	file << this->getRadius() << " ";

	return true;

}